/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the demonstration applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "composition.h"

#include <QBoxLayout>
#include <QRadioButton>
#include <QTimer>
#include <QDateTime>
#include <QSlider>
#include <QMouseEvent>
#include <QCoreApplication>
#include <QString>
#include <qmath.h>
#include <iostream>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <malloc.h>


#include <MemoryManager.h>

const int animationInterval = 15; // update every 16 ms = ~60FPS

SpeculationTaskCompositionRenderer::SpeculationTaskCompositionRenderer( string n, 
									CompositionRenderer *view,
									funcPtr task )
: SpeculationTask( n )
{
	m_view = view;
	m_task = task;
}

SpeculationTaskCompositionRenderer::~SpeculationTaskCompositionRenderer()
{
}

void SpeculationTaskCompositionRenderer::execute()
{
	(*m_view.*m_task)();
}

CompositionRenderer *g_globalRenderer;

void CompositionWidget::idleProcessing()
{
    if( m_recordEvents == 2 )
    {
		timeval currentTime, current;
		gettimeofday(&currentTime,NULL);
		timersub(&currentTime,&m_startClock,&current);
		double currentD = current.tv_sec + current.tv_usec/1000000.0;

		if( m_commandTimeList[m_commandListIndex] <= currentD )
		{
			printf("Time %lf\n", currentD );
			issueCommand( m_commandList[m_commandListIndex] );
			gettimeofday(&m_startClock,NULL);
			m_commandListIndex++;
		}

        if( m_commandListIndex == m_commandList.size() )
        {
        	exit(0);
        }

    }

	// Run the speculation manager
	m_speculationManager->run();
}

void CompositionWidget::issueCommand(string command)
{
    if( !strcmp(command.c_str(), "CompositionRenderer::setBlur1()") )
    {
		SpeculationTask *ret = new SpeculationTaskCompositionRenderer( 
			"CompositionRenderer::setBlur1()", 
			g_globalRenderer, 
			&CompositionRenderer::setBlur1 );
        ret->execute();
        delete ret;
        return;
    }

    if( !strcmp(command.c_str(), "CompositionRenderer::setBlur2()") )
	{
		SpeculationTask *ret = new SpeculationTaskCompositionRenderer( 
			"CompositionRenderer::setBlur2()", 
			g_globalRenderer, 
			&CompositionRenderer::setBlur2 );
        ret->execute();
        delete ret;
        return;
	}

    if( !strcmp(command.c_str(), "CompositionRenderer::setBlurImplode()") )
	{
		SpeculationTask *ret = new SpeculationTaskCompositionRenderer( 
			"CompositionRenderer::setBlurImplode()", 
			g_globalRenderer, 
			&CompositionRenderer::setBlurImplode );
        ret->execute();
        delete ret;
        return;
	}

    if( !strcmp(command.c_str(), "CompositionRenderer::setOilPaint()") )
	{
		SpeculationTask *ret = new SpeculationTaskCompositionRenderer( 
			"CompositionRenderer::setOilPaint()", 
			g_globalRenderer, 
			&CompositionRenderer::setOilPaint );
        ret->execute();
        delete ret;
        return;
	}

    if( !strcmp(command.c_str(), "CompositionRenderer::setHaldBlur()") )
	{
		SpeculationTask *ret = new SpeculationTaskCompositionRenderer( 
			"CompositionRenderer::setHaldBlur()", 
			g_globalRenderer, 
			&CompositionRenderer::setHaldBlur );
        ret->execute();
        delete ret;
        return;
	}

    if( !strcmp(command.c_str(), "CompositionRenderer::setDft()") )
	{
		SpeculationTask *ret = new SpeculationTaskCompositionRenderer( 
			"CompositionRenderer::setDft()", 
			g_globalRenderer, 
			&CompositionRenderer::setDft );
        ret->execute();
        delete ret;
        return;
	}
}

int roundRobin = 0;

SpeculationTask *CompositionWidget::generateSpeculation()
{
//	int roundRobin = rand() % 5;
	roundRobin %= 5;
	SpeculationTask* ret = 0;

	if( roundRobin == 0 )
	{
		ret = new SpeculationTaskCompositionRenderer( 
			"CompositionRenderer::setBlur1()", 
			g_globalRenderer, 
			&CompositionRenderer::blur1 );
	}
	else if( roundRobin == 1 )
	{
		ret = new SpeculationTaskCompositionRenderer( 
			"CompositionRenderer::setBlur2()", 
			g_globalRenderer, 
			&CompositionRenderer::blur2 );
	}
	else if( roundRobin == 2 )
	{
		ret = new SpeculationTaskCompositionRenderer( 
			"CompositionRenderer::setBlurImplode()", 
			g_globalRenderer, 
			&CompositionRenderer::blurImplode );
	}
	else if( roundRobin == 3 )
	{
		ret = new SpeculationTaskCompositionRenderer( 
			"CompositionRenderer::setOilPaint()", 
			g_globalRenderer, 
			&CompositionRenderer::oilPaint );
	}
	else if( roundRobin == 4 )
	{
		ret = new SpeculationTaskCompositionRenderer( 
			"CompositionRenderer::setHaldBlur()", 
			g_globalRenderer, 
			&CompositionRenderer::haldBlur );
	}
	else if( roundRobin == 5 )
	{
		ret = new SpeculationTaskCompositionRenderer( 
			"CompositionRenderer::setDft()", 
			g_globalRenderer, 
			&CompositionRenderer::dft );
	}

	roundRobin++;

	return ret;
}

CompositionWidget::CompositionWidget(QWidget *parent, int argc, char **argv)
  : QWidget(parent), cbImage( 0 )
{

	m_recordEvents = 0;
	int maxTasks = 0;
	int maxRunningTasks = 0;
	char *playbackFileName = 0;

	for( int i = 1 ; i < argc ; ++i )
    {    
        if( !strcmp(argv[i], "-recordEvents" ) )
        {
            printf("Record events\n");
            m_recordEvents = 1;
        }

        if( !strcmp(argv[i], "-playbackEvents" ) )
        {
            printf("Playback events\n");
            m_recordEvents = 2;
			playbackFileName = argv[++i];
        }

        if( !strcmp(argv[i], "-ntasks" ) )
        {
			maxRunningTasks = atoi(argv[++i]);
			maxTasks = atoi(argv[++i]);
            printf("Running tasks = %d\n", maxRunningTasks);
            printf("Total tasks = %d\n", maxTasks);
        }
    }

    CompositionRenderer *view = new CompositionRenderer(this, m_recordEvents);
	g_globalRenderer = view;

    QGroupBox *mainGroup = new QGroupBox(parent);
    mainGroup->setTitle(tr("Composition Modes"));

    QGroupBox *modesGroup = new QGroupBox(mainGroup);
    modesGroup->setTitle(tr("Mode"));

    rbClear = new QRadioButton(tr("Clear"), modesGroup);
    connect(rbClear, SIGNAL(clicked()), view, SLOT(setClearMode()));
    rbSource = new QRadioButton(tr("Source"), modesGroup);
    connect(rbSource, SIGNAL(clicked()), view, SLOT(setSourceMode()));
    rbDest = new QRadioButton(tr("Destination"), modesGroup);
    connect(rbDest, SIGNAL(clicked()), view, SLOT(setDestMode()));
    rbSourceOver = new QRadioButton(tr("Source Over"), modesGroup);
    connect(rbSourceOver, SIGNAL(clicked()), view, SLOT(setSourceOverMode()));
    rbDestOver = new QRadioButton(tr("Destination Over"), modesGroup);
    connect(rbDestOver, SIGNAL(clicked()), view, SLOT(setDestOverMode()));
    rbSourceIn = new QRadioButton(tr("Source In"), modesGroup);
    connect(rbSourceIn, SIGNAL(clicked()), view, SLOT(setSourceInMode()));
//     rbDestIn = new QRadioButton(tr("Dest In"), modesGroup);
//     connect(rbDestIn, SIGNAL(clicked()), view, SLOT(setDestInMode()));
//     rbSourceOut = new QRadioButton(tr("Source Out"), modesGroup);
//     connect(rbSourceOut, SIGNAL(clicked()), view, SLOT(setSourceOutMode()));
//     rbDestOut = new QRadioButton(tr("Dest Out"), modesGroup);
//     connect(rbDestOut, SIGNAL(clicked()), view, SLOT(setDestOutMode()));
//     rbSourceAtop = new QRadioButton(tr("Source Atop"), modesGroup);
//     connect(rbSourceAtop, SIGNAL(clicked()), view, SLOT(setSourceAtopMode()));
//     rbDestAtop = new QRadioButton(tr("Dest Atop"), modesGroup);
//     connect(rbDestAtop, SIGNAL(clicked()), view, SLOT(setDestAtopMode()));
//     rbXor = new QRadioButton(tr("Xor"), modesGroup);
//     connect(rbXor, SIGNAL(clicked()), view, SLOT(setXorMode()));

    rbPlus = new QRadioButton(tr("Plus"), modesGroup);
    connect(rbPlus, SIGNAL(clicked()), view, SLOT(setPlusMode()));
    rbMultiply = new QRadioButton(tr("Multiply"), modesGroup);
    connect(rbMultiply, SIGNAL(clicked()), view, SLOT(setMultiplyMode()));
    rbScreen = new QRadioButton(tr("Screen"), modesGroup);
    connect(rbScreen, SIGNAL(clicked()), view, SLOT(setScreenMode()));
    rbOverlay = new QRadioButton(tr("Overlay"), modesGroup);
    connect(rbOverlay, SIGNAL(clicked()), view, SLOT(setOverlayMode()));
    rbDarken = new QRadioButton(tr("Darken"), modesGroup);
    connect(rbDarken, SIGNAL(clicked()), view, SLOT(setDarkenMode()));
    rbLighten = new QRadioButton(tr("Lighten"), modesGroup);
    connect(rbLighten, SIGNAL(clicked()), view, SLOT(setLightenMode()));
//     rbColorDodge = new QRadioButton(tr("Color Dodge"), modesGroup);
//     connect(rbColorDodge, SIGNAL(clicked()), view, SLOT(setColorDodgeMode()));
//     rbColorBurn = new QRadioButton(tr("Color Burn"), modesGroup);
//     connect(rbColorBurn, SIGNAL(clicked()), view, SLOT(setColorBurnMode()));
//     rbHardLight = new QRadioButton(tr("Hard Light"), modesGroup);
//     connect(rbHardLight, SIGNAL(clicked()), view, SLOT(setHardLightMode()));
//     rbSoftLight = new QRadioButton(tr("Soft Light"), modesGroup);
//     connect(rbSoftLight, SIGNAL(clicked()), view, SLOT(setSoftLightMode()));
//     rbDifference = new QRadioButton(tr("Difference"), modesGroup);
//     connect(rbDifference, SIGNAL(clicked()), view, SLOT(setDifferenceMode()));
//     rbExclusion = new QRadioButton(tr("Exclusion"), modesGroup);
//     connect(rbExclusion, SIGNAL(clicked()), view, SLOT(setExclusionMode()));

    // new stuff
    QGroupBox *effectsGroup = new QGroupBox(mainGroup);
    effectsGroup->setTitle(tr("Effects"));






    rbBlur1 = new QPushButton(tr("Blur1"), effectsGroup);
    connect(rbBlur1, SIGNAL(clicked()), view, SLOT(setBlur1()));

    rbBlur2 = new QPushButton(tr("Blur2"), effectsGroup);
    connect(rbBlur2, SIGNAL(clicked()), view, SLOT(setBlur2()));

    rbBlurImplode = new QPushButton(tr("BlurImplode"), effectsGroup);
    connect(rbBlurImplode, SIGNAL(clicked()), view, SLOT(setBlurImplode()));

    rbOilPaint = new QPushButton(tr("OilPaint"), effectsGroup);
    connect(rbOilPaint, SIGNAL(clicked()), view, SLOT(setOilPaint()));

    rbDft = new QPushButton(tr("Dft"), effectsGroup);
    connect(rbDft, SIGNAL(clicked()), view, SLOT(setDft()));

    rbHaldBlur = new QPushButton(tr("HaldBlur"), effectsGroup);
    connect(rbHaldBlur, SIGNAL(clicked()), view, SLOT(setHaldBlur()));




    QGroupBox *imageGroup = new QGroupBox(mainGroup);
    imageGroup->setTitle(tr("Image"));

    cbImage = new QComboBox( imageGroup );
    connect(cbImage, SIGNAL(currentIndexChanged(int)), view, SLOT(setInputImage(int)));

    
    QGroupBox *circleColorGroup = new QGroupBox(mainGroup);
    circleColorGroup->setTitle(tr("Circle color"));
    QSlider *circleColorSlider = new QSlider(Qt::Horizontal, circleColorGroup);
    circleColorSlider->setRange(0, 359);
    circleColorSlider->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
    connect(circleColorSlider, SIGNAL(valueChanged(int)), view, SLOT(setCircleColor(int)));

    QGroupBox *circleAlphaGroup = new QGroupBox(mainGroup);
    circleAlphaGroup->setTitle(tr("Circle alpha"));
    QSlider *circleAlphaSlider = new QSlider(Qt::Horizontal, circleAlphaGroup);
    circleAlphaSlider->setRange(0, 255);
    circleAlphaSlider->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
    connect(circleAlphaSlider, SIGNAL(valueChanged(int)), view, SLOT(setCircleAlpha(int)));

//     QPushButton *showSourceButton = new QPushButton(mainGroup);
//     showSourceButton->setText(tr("Show Source"));
// #if defined(QT_OPENGL_SUPPORT) && !defined(QT_OPENGL_ES)
//     QPushButton *enableOpenGLButton = new QPushButton(mainGroup);
//     enableOpenGLButton->setText(tr("Use OpenGL"));
//     enableOpenGLButton->setCheckable(true);
//     enableOpenGLButton->setChecked(view->usesOpenGL());

//     if (!QGLFormat::hasOpenGL() || !QGLPixelBuffer::hasOpenGLPbuffers())
//         enableOpenGLButton->hide();
// #endif
//     QPushButton *whatsThisButton = new QPushButton(mainGroup);
//     whatsThisButton->setText(tr("What's This?"));
//     whatsThisButton->setCheckable(true);

//     QPushButton *animateButton = new QPushButton(mainGroup);
//     animateButton->setText(tr("Animated"));
//     animateButton->setCheckable(true);
//     animateButton->setChecked(true);

    QHBoxLayout *viewLayout = new QHBoxLayout(this);
    viewLayout->addWidget(view);
    viewLayout->addWidget(mainGroup);

    QVBoxLayout *mainGroupLayout = new QVBoxLayout(mainGroup);
    mainGroupLayout->addWidget(circleColorGroup);
    mainGroupLayout->addWidget(circleAlphaGroup);
    mainGroupLayout->addWidget(modesGroup);
    mainGroupLayout->addWidget(effectsGroup);
    mainGroupLayout->addWidget(imageGroup);
//     mainGroupLayout->addStretch();
//     mainGroupLayout->addWidget(animateButton);
//     mainGroupLayout->addWidget(whatsThisButton);
//     mainGroupLayout->addWidget(showSourceButton);
// #if defined(QT_OPENGL_SUPPORT) && !defined(QT_OPENGL_ES)
//     mainGroupLayout->addWidget(enableOpenGLButton);
// #endif

    QGridLayout *modesLayout = new QGridLayout(modesGroup);
    modesLayout->addWidget(rbClear,             0,      0);
    modesLayout->addWidget(rbSource,            1,      0);
    modesLayout->addWidget(rbDest,              2,      0);
    modesLayout->addWidget(rbSourceOver,        3,      0);
    modesLayout->addWidget(rbDestOver,          4,      0);
    modesLayout->addWidget(rbSourceIn,          5,      0);
//     modesLayout->addWidget(rbDestIn,            6,      0);
//     modesLayout->addWidget(rbSourceOut,         7,      0);
//     modesLayout->addWidget(rbDestOut,           8,      0);
//     modesLayout->addWidget(rbSourceAtop,        9,      0);
//     modesLayout->addWidget(rbDestAtop,         10,      0);
//     modesLayout->addWidget(rbXor,              11,      0);

    modesLayout->addWidget(rbPlus,              0,      1);
    modesLayout->addWidget(rbMultiply,          1,      1);
    modesLayout->addWidget(rbScreen,            2,      1);
    modesLayout->addWidget(rbOverlay,           3,      1);
    modesLayout->addWidget(rbDarken,            4,      1);
    modesLayout->addWidget(rbLighten,           5,      1);
//     modesLayout->addWidget(rbColorDodge,        6,      1);
//     modesLayout->addWidget(rbColorBurn,         7,      1);
//     modesLayout->addWidget(rbHardLight,         8,      1);
//     modesLayout->addWidget(rbSoftLight,         9,      1);
//     modesLayout->addWidget(rbDifference,       10,      1);
//     modesLayout->addWidget(rbExclusion,        11,      1);

    QGridLayout *effectsLayout = new QGridLayout(effectsGroup);
    effectsLayout->addWidget(rbBlur1,        0,      0);
    effectsLayout->addWidget(rbBlur2,        1,      0);
    effectsLayout->addWidget(rbBlurImplode,  2,      0);
    effectsLayout->addWidget(rbOilPaint,     0,      1);
    effectsLayout->addWidget(rbDft,          1,      1);
    effectsLayout->addWidget(rbHaldBlur,     2,      1);


    QVBoxLayout *imageGroupLayout = new QVBoxLayout(imageGroup);
    imageGroupLayout->addWidget(cbImage);
    view->addImagesToCombo( cbImage );


    QVBoxLayout *circleColorLayout = new QVBoxLayout(circleColorGroup);
    circleColorLayout->addWidget(circleColorSlider);

    QVBoxLayout *circleAlphaLayout = new QVBoxLayout(circleAlphaGroup);
    circleAlphaLayout->addWidget(circleAlphaSlider);

    view->loadDescription(":res/composition/composition.html");
    view->loadSourceFile(":res/composition/composition.cpp");

//     connect(whatsThisButton, SIGNAL(clicked(bool)), view, SLOT(setDescriptionEnabled(bool)));
//     connect(view, SIGNAL(descriptionEnabledChanged(bool)), whatsThisButton, SLOT(setChecked(bool)));
//     connect(showSourceButton, SIGNAL(clicked()), view, SLOT(showSource()));
// #if defined(QT_OPENGL_SUPPORT) && !defined(QT_OPENGL_ES)
//     connect(enableOpenGLButton, SIGNAL(clicked(bool)), view, SLOT(enableOpenGL(bool)));
// #endif
    //connect(animateButton, SIGNAL(toggled(bool)), view, SLOT(setAnimationEnabled(bool)));

    circleColorSlider->setValue(270);
    circleAlphaSlider->setValue(200);
    rbSource->animateClick();

    setWindowTitle(tr("Composition Modes"));

    if( m_recordEvents == 2 )
    {
        string str;
        ifstream input(playbackFileName);

	    while( getline(input,str) )
	    {
		    istringstream in(str);
		    string s;
            double t;
		    in>>s;
            m_commandList.push_back(s);
		    in>>t;
            m_commandTimeList.push_back(t);

            printf("%s %lf\n", s.c_str(), t );
	    }
        m_commandListIndex = 0;
    }

	// Speculation manager

	m_speculationManager = SpeculationManager::Instance();
	m_speculationManager->setRunningTasks(maxRunningTasks);
	m_speculationManager->setTotalTasks(maxTasks);
	m_speculationManager->setSpeculationGenerator(generateSpeculation);

    m_startClock = m_speculationManager->getStartClock();
    view->m_startClock = m_speculationManager->getStartClock();

	QTimer *timer = new QTimer(this);
	connect(timer, SIGNAL(timeout()), this, SLOT(idleProcessing()));
	timer->start(20);
}

CompositionWidget::~CompositionWidget()
{
	delete m_speculationManager;
}

void CompositionWidget::nextMode()
{
    /*
      if (!m_animation_enabled)
      return;
      if (rbClear->isChecked()) rbSource->animateClick();
      if (rbSource->isChecked()) rbDest->animateClick();
      if (rbDest->isChecked()) rbSourceOver->animateClick();
      if (rbSourceOver->isChecked()) rbDestOver->animateClick();
      if (rbDestOver->isChecked()) rbSourceIn->animateClick();
      if (rbSourceIn->isChecked()) rbDestIn->animateClick();
      if (rbDestIn->isChecked()) rbSourceOut->animateClick();
      if (rbSourceOut->isChecked()) rbDestOut->animateClick();
      if (rbDestOut->isChecked()) rbSourceAtop->animateClick();
      if (rbSourceAtop->isChecked()) rbDestAtop->animateClick();
      if (rbDestAtop->isChecked()) rbXor->animateClick();
      if (rbXor->isChecked()) rbClear->animateClick();
    */
}

CompositionRenderer::CompositionRenderer(QWidget *parent, int recordEvents)
  : ArthurFrame(parent), m_recordEvents( recordEvents ), m_forceRedraw( true )
{
    m_animation_enabled = true;
    m_animationTimer = startTimer(animationInterval);

    // Install the memory watche to keep track of the pages allocated
    // by the background image
    installMemoryWatcher();

    m_image = new QImage(512,512,QImage::Format_RGB32);

    // Remove the memory watcher, we only want to keep track of the background image
    removeMemoryWatcher();

    loadTestImages();

    m_circle_alpha = 127;
    m_circle_hue = 255;
    m_current_object = NoObject;
    m_composition_mode = QPainter::CompositionMode_SourceOut;

    m_circle_pos = QPoint(200, 100);

    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
#ifdef QT_OPENGL_SUPPORT
    m_pbuffer = 0;
    m_pbuffer_size = 1024;
#endif

    if( m_recordEvents == 1 )
    {
        m_commandQueue = fopen("commandQueue.txt","w");
    }

    gettimeofday(&m_startClock,NULL);

    char commandLogFilename[1024];

    sprintf(commandLogFilename,"commandLog_%d.csv", (int)m_startClock.tv_sec);

    m_commandLog = fopen(commandLogFilename,"w");
}

CompositionRenderer::~CompositionRenderer()
{
    fclose(m_commandLog);

    if( m_recordEvents == 1 )
    {
        fclose( m_commandQueue );
    }
}

QRectF rectangle_around(const QPointF &p, const QSizeF &size = QSize(250, 200))
{
    QRectF rect(p, size);
    rect.translate(-size.width()/2, -size.height()/2);
    return rect;
}

void CompositionRenderer::setAnimationEnabled(bool enabled)
{
    if (m_animation_enabled == enabled)
        return;
    m_animation_enabled = enabled;
    if (enabled) {
        Q_ASSERT(!m_animationTimer);
        m_animationTimer = startTimer(animationInterval);
    } else {
        killTimer(m_animationTimer);
        m_animationTimer = 0;
    }
}

void CompositionRenderer::updateCirclePos()
{
    if (m_current_object != NoObject)
        return;
    QDateTime dt = QDateTime::currentDateTime();
    qreal t = (dt.toTime_t() * 1000 + dt.time().msec()) / 1000.0;

    qreal x = width() / qreal(2) + (qCos(t*8/11) + qSin(-t)) * width() / qreal(4);
    qreal y = height() / qreal(2) + (qSin(t*6/7) + qCos(t * qreal(1.5))) * height() / qreal(4);

    setCirclePos(QLineF(m_circle_pos, QPointF(x, y)).pointAt(0.02));
}

void CompositionRenderer::drawBase(QPainter &p)
{
    p.setPen(Qt::NoPen);

    QLinearGradient rect_gradient(0, 0, 0, height());
    rect_gradient.setColorAt(0, Qt::red);
    rect_gradient.setColorAt(.17, Qt::yellow);
    rect_gradient.setColorAt(.33, Qt::green);
    rect_gradient.setColorAt(.50, Qt::cyan);
    rect_gradient.setColorAt(.66, Qt::blue);
    rect_gradient.setColorAt(.81, Qt::magenta);
    rect_gradient.setColorAt(1, Qt::red);
    p.setBrush(rect_gradient);
    p.drawRect(width() / 2, 0, width() / 2, height());

    QLinearGradient alpha_gradient(0, 0, width(), 0);
    alpha_gradient.setColorAt(0, Qt::white);
    alpha_gradient.setColorAt(0.2, Qt::white);
    alpha_gradient.setColorAt(0.5, Qt::transparent);
    alpha_gradient.setColorAt(0.8, Qt::white);
    alpha_gradient.setColorAt(1, Qt::white);

    p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
    p.setBrush(alpha_gradient);
    p.drawRect(0, 0, width(), height());

    p.setCompositionMode(QPainter::CompositionMode_DestinationOver);

    p.setPen(Qt::NoPen);
    p.setRenderHint(QPainter::SmoothPixmapTransform);
#ifdef Q_WS_QWS
    p.drawPixmap(rect(), m_image);
#else
    p.drawImage(rect(), *m_image);

    const QRect &r( rect() );
    printf( "just drew m_image...for rect %d, %d, %d, %d (lrtb)\n",
	    r.left(), r.right(), r.top(), r.bottom() );
#endif
}

void CompositionRenderer::drawSource(QPainter &p)
{
    p.setPen(Qt::NoPen);
    p.setRenderHint(QPainter::Antialiasing);
    p.setCompositionMode(m_composition_mode);

    QRectF circle_rect = rectangle_around(m_circle_pos);
    QColor color = QColor::fromHsvF(m_circle_hue / 360.0, 1, 1, m_circle_alpha / 255.0);
    QLinearGradient circle_gradient(circle_rect.topLeft(), circle_rect.bottomRight());
    circle_gradient.setColorAt(0, color.light());
    circle_gradient.setColorAt(0.5, color);
    circle_gradient.setColorAt(1, color.dark());
    p.setBrush(circle_gradient);

    p.drawEllipse(circle_rect);
}

void CompositionRenderer::paint(QPainter *painter)
{

#if defined(QT_OPENGL_SUPPORT) && !defined(QT_OPENGL_ES)
    if (usesOpenGL()) {

        int new_pbuf_size = m_pbuffer_size;
        if (size().width() > m_pbuffer_size ||
            size().height() > m_pbuffer_size)
            new_pbuf_size *= 2;

        if (size().width() < m_pbuffer_size/2 &&
            size().height() < m_pbuffer_size/2)
            new_pbuf_size /= 2;

        if (!m_pbuffer || new_pbuf_size != m_pbuffer_size) {
            if (m_pbuffer) {
                m_pbuffer->deleteTexture(m_base_tex);
                m_pbuffer->deleteTexture(m_compositing_tex);
                delete m_pbuffer;
            }

            m_pbuffer = new QGLPixelBuffer(QSize(new_pbuf_size, new_pbuf_size), QGLFormat::defaultFormat(), glWidget());
            m_pbuffer->makeCurrent();
            m_base_tex = m_pbuffer->generateDynamicTexture();
            m_compositing_tex = m_pbuffer->generateDynamicTexture();
            m_pbuffer_size = new_pbuf_size;
        }

        if (size() != m_previous_size) {
            m_previous_size = size();
            QPainter p(m_pbuffer);
            p.setCompositionMode(QPainter::CompositionMode_Source);
            p.fillRect(QRect(0, 0, m_pbuffer->width(), m_pbuffer->height()), Qt::transparent);
            drawBase(p);
            p.end();
            m_pbuffer->updateDynamicTexture(m_base_tex);
        }

        qreal x_fraction = width()/float(m_pbuffer->width());
        qreal y_fraction = height()/float(m_pbuffer->height());
	cout << "+++++drawing with fractions x and y " << x_fraction << "," << y_fraction << endl;

        {
            QPainter p(m_pbuffer);
            p.setCompositionMode(QPainter::CompositionMode_Source);
            p.fillRect(QRect(0, 0, m_pbuffer->width(), m_pbuffer->height()), Qt::transparent);

            p.save(); // Needed when using the GL1 engine
            p.beginNativePainting(); // Needed when using the GL2 engine

            glBindTexture(GL_TEXTURE_2D, m_base_tex);
            glEnable(GL_TEXTURE_2D);
            glColor4f(1.,1.,1.,1.);

            glBegin(GL_QUADS);
            {
                glTexCoord2f(0, 1.0);
                glVertex2f(0, 0);

                glTexCoord2f(x_fraction, 1.0);
                glVertex2f(width(), 0);

                glTexCoord2f(x_fraction, 1.0-y_fraction);
                glVertex2f(width(), height());

                glTexCoord2f(0, 1.0-y_fraction);
                glVertex2f(0, height());
            }
            glEnd();

            glDisable(GL_TEXTURE_2D);

            p.endNativePainting(); // Needed when using the GL2 engine
            p.restore(); // Needed when using the GL1 engine

            drawSource(p);
            p.end();
            m_pbuffer->updateDynamicTexture(m_compositing_tex);
        }

        painter->beginNativePainting(); // Needed when using the GL2 engine
        glWidget()->makeCurrent(); // Needed when using the GL1 engine
        glBindTexture(GL_TEXTURE_2D, m_compositing_tex);
        glEnable(GL_TEXTURE_2D);
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
        glColor4f(1.,1.,1.,1.);
        glBegin(GL_QUADS);
        {
            glTexCoord2f(0, 1.0);
            glVertex2f(0, 0);

            glTexCoord2f(x_fraction, 1.0);
            glVertex2f(width(), 0);

            glTexCoord2f(x_fraction, 1.0-y_fraction);
            glVertex2f(width(), height());

            glTexCoord2f(0, 1.0-y_fraction);
            glVertex2f(0, height());
        }
        glEnd();
        glDisable(GL_TEXTURE_2D);
        painter->endNativePainting(); // Needed when using the GL2 engine
    } else
#endif
    {
    // using a QImage
    if (m_buffer.size() != size() || m_forceRedraw )
	{
	  if( m_forceRedraw ) // invalidate the whole draw region
	    this->update( rect() );

#ifdef Q_WS_QWS
            m_base_buffer = QPixmap(size());
            m_base_buffer.fill(Qt::transparent);
#else
            m_buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied);
            m_base_buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied);

            m_base_buffer.fill(0);
#endif

            QPainter p(&m_base_buffer);
            drawBase(p);
	        m_forceRedraw = false;
        }

#ifdef Q_WS_QWS
	cout << "IN Q_WS_QWS case in the paint routine" << endl;
        m_buffer = m_base_buffer;
#else
        memcpy(m_buffer.bits(), m_base_buffer.bits(), m_buffer.byteCount());
#endif

        {
            QPainter p(&m_buffer);
            drawSource(p);
        }

#ifdef Q_WS_QWS
        painter->drawPixmap(0, 0, m_buffer);
#else
        painter->drawImage(0, 0, m_buffer);
#endif
    }
}

void CompositionRenderer::mousePressEvent(QMouseEvent *e)
{
    setDescriptionEnabled(false);

    QRectF circle = rectangle_around(m_circle_pos);

    if (circle.contains(e->pos())) {
        m_current_object = Circle;
        m_offset = circle.center() - e->pos();
    } else {
        m_current_object = NoObject;
    }
    if (m_animation_enabled) {
        killTimer(m_animationTimer);
        m_animationTimer = 0;
    }
}

void CompositionRenderer::mouseMoveEvent(QMouseEvent *e)
{
    if (m_current_object == Circle) setCirclePos(e->pos() + m_offset);
}

void CompositionRenderer::mouseReleaseEvent(QMouseEvent *)
{
    m_current_object = NoObject;

    if (m_animation_enabled) {
        Q_ASSERT(!m_animationTimer);
        m_animationTimer = startTimer(animationInterval);
    }
}

void CompositionRenderer::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == m_animationTimer)
        updateCirclePos();
}

void CompositionRenderer::setCirclePos(const QPointF &pos)
{
    const QRect oldRect = rectangle_around(m_circle_pos).toAlignedRect();
    m_circle_pos = pos;
    const QRect newRect = rectangle_around(m_circle_pos).toAlignedRect();
#if defined(QT_OPENGL_SUPPORT) && !defined(QT_OPENGL_ES)
    if (usesOpenGL())
        update();
    else
#endif
        update(oldRect | newRect);
}

void
CompositionRenderer::convertIMtoQImage( QImage &qimage, Magick::Image &image )
{
   Magick::ColorRGB rgb;

   const int qheight = qimage.height();
   const int qwidth  = qimage.width();
   
   for( int row = 0; row < qheight; ++row ) 
   {
      const Magick::PixelPacket* const  pixels = image.getConstPixels( 0, row, qwidth, 1 );
      const uchar *currentRow = qimage.scanLine(row);
      uint *myCurrentRow = (uint*)currentRow;

      for( int col = 0; col < qwidth; ++col ) 
      {
         rgb = *( pixels + col );
         const QRgb qrgb = qRgb( static_cast<int>( 255 * rgb.red  () ),
                                 static_cast<int>( 255 * rgb.green() ),
                                 static_cast<int>( 255 * rgb.blue () ) );
        *myCurrentRow++ = qrgb;
      }
   }
}

void
CompositionRenderer::convertQImagetoIM( Magick::Image &image, QImage &qimage )
{
   const int qheight = qimage.height();
   const int qwidth  = qimage.width();

   for( int row = 0; row < qheight; ++row )
   {
      Magick::PixelPacket *pixels = image.getPixels( 0, row, qwidth, 1 );
      const uchar *currentRow = qimage.scanLine(row);
      uint *myCurrentRow = (uint*)currentRow;

      for( int col = 0; col < qwidth; ++col )
      {
    	 QRgb qrgb = *myCurrentRow++;
    	 QColor crgb = QColor( qrgb );

    	 Magick::ColorRGB irgb( crgb.redF(), crgb.greenF(), crgb.blueF() );

         *( pixels + col ) = irgb;
      }
   }
}


void
CompositionRenderer::loadTestImages()
{
  QString appPath = QCoreApplication::applicationDirPath();
  QDir dir( appPath );

  dir.cdUp();
  if( !dir.cd( "images" ) )
  {
      printf( "Unable to find images directory expected at %s\n:",
              dir.absolutePath().toStdString().c_str() );
      return;
  }

  QStringList filters;
  filters << "*.tiff" ;
  dir.setNameFilters( filters );

  QStringList l = dir.entryList();
  QStringList::const_iterator it, itEnd( l.end() );
  for( it = l.begin(); it != itEnd; ++it )
  {
      Magick::Image i;
      QString n( dir.absolutePath() );
      n += dir.separator();
      n += *it;
      i.read( n.toStdString().c_str() );
      std::cout << "Just read img: " << n.toStdString() << std::endl;
      
      m_Images.push_back( i );
      m_ImageNames.push_back( n );

  }

  // TODO !!! if we failed to load any images, handle that case
  assert( !m_Images.empty() );

  cout << "Setting input image to image zero and number of images loaded is: " << m_Images.size() << endl;
  setInputImage( 0 ); // set the default image on load

}

void
CompositionRenderer::addImagesToCombo( QComboBox *cbImage )
{
  if( !cbImage )
    return;

      // add images to the combo box widget
  std::list<QString>::iterator it, itEnd( m_ImageNames.end() );

  for( it = m_ImageNames.begin(); it != itEnd; ++it )
  {
    cout << "adding item to combo" << it->toStdString() << endl;
    cbImage->addItem( *it );
  }
  
}



//
//  Callback for choosing the image to operate on
//
void
CompositionRenderer::setInputImage( int index )
{

  if( index > (int)m_Images.size() - 1 )
  {
    cout << "UI error: image " << index << " out of range: " <<
      m_Images.size() << endl;

    return;
  }

  SpeculationManager *specMan = SpeculationManager::Instance();
  specMan->killAllSpeculations();
   
  // resets the image to the "new" image // should also cancel speculations!! TODO
  cout << "Setting image at index: " << index << endl;

  m_imImage = m_Images[index];

  convertIMtoQImage( *m_image, m_imImage );

  m_forceRedraw = true;

}


// void
// CompositionRenderer

//   m_forceRedraw = true;
  
// }

void CompositionRenderer::refresh()
{
   Magick::Image &i( getCurrentImage() );
   convertIMtoQImage( *m_image, i );

   m_forceRedraw = true;
}

Magick::Image &
CompositionRenderer::getRandomImage()
{
    if( m_Images.empty() )
        return m_imImage;

    // not random, just sequential...
    static int last( 0 );
    
    unsigned int mod = static_cast<unsigned int>( m_Images.size() );
    
    int idx = last % mod;
    ++last;
    return m_Images[idx];
}


Magick::Image &
CompositionRenderer::getCurrentImage()
{
	convertQImagetoIM( m_imImage, *m_image );
	return m_imImage;
}

void
CompositionRenderer::blur1()
{

    Magick::Image &i( getCurrentImage() );

    try
    {
        i.gaussianBlur( 10, 0.5 );
        convertIMtoQImage( *m_image, i );
        m_forceRedraw = true;
    }
    catch( Magick::Exception &err )
    {
        printf(err.what());
        printf("\n");
        QMessageBox::warning(this, tr("SpecEx"),
                                       tr(err.what()),
                                       QMessageBox::Ok,
                                       QMessageBox::Ok);
    }


}

void
CompositionRenderer::blur2()
{

    Magick::Image &i( getCurrentImage() );

    try
    {
        i.gaussianBlur( 15, 1.0 );
        convertIMtoQImage( *m_image, i );
        m_forceRedraw = true;
    }
    catch( Magick::Exception &err )
    {
        printf(err.what());
        printf("\n");
        QMessageBox::warning(this, tr("SpecEx"),
                                       tr(err.what()),
                                       QMessageBox::Ok,
                                       QMessageBox::Ok);
    }
}

void
CompositionRenderer::blurImplode()
{
    Magick::Image &i( getCurrentImage() );

    try
    {
        i.gaussianBlur( 10,0.5 );
        i.implode( .3 );
        convertIMtoQImage( *m_image, i );
        m_forceRedraw = true;
    }
    catch( Magick::Exception &err )
    {
        printf(err.what());
        printf("\n");
        QMessageBox::warning(this, tr("SpecEx"),
                                       tr(err.what()),
                                       QMessageBox::Ok,
                                       QMessageBox::Ok);
    }
}

void
CompositionRenderer::oilPaint()
{
	Magick::Image &i( getCurrentImage() );

    try
    {
        i.gaussianBlur( 5, 0.5 );
        i.oilPaint( 10 );
        convertIMtoQImage( *m_image, i );
        m_forceRedraw = true;
    }
    catch( Magick::Exception &err )
    {
        printf(err.what());
        printf("\n");
        QMessageBox::warning(this, tr("SpecEx"),
                                       tr(err.what()),
                                       QMessageBox::Ok,
                                       QMessageBox::Ok);
    }

}


void
CompositionRenderer::dft()
{
//    Magick::Image &i( getCurrentImage() );
//    Magick::Image &j( getRandomImage() );

    try
    {
    //    i.inverseFourierTransform( j, true ); // do magnitude here...
    //    m_image = convertIMtoQImage( i );
        m_forceRedraw = true;
    }
    catch( Magick::Exception &err )
    {
        printf(err.what());
        printf("\n");
        QMessageBox::warning(this, tr("SpecEx"),
                                       tr(err.what()),
                                       QMessageBox::Ok,
                                       QMessageBox::Ok);
    }
}



void
CompositionRenderer::haldBlur()
{
    Magick::Image &i( getCurrentImage() );
//    Magick::Image &j( getRandomImage() );

    try
    {
        i.gaussianBlur( 10, .5 );
        //i.haldClut( j );
		i.solarize( 50.0 );
        convertIMtoQImage( *m_image, i );
        m_forceRedraw = true;
    }
    catch( Magick::Exception &err )
    {
        printf(err.what());
        printf("\n");
        QMessageBox::warning(this, tr("SpecEx"),
                                       tr(err.what()),
                                       QMessageBox::Ok,
                                       QMessageBox::Ok);
    }
}



void
CompositionRenderer::setBlur1()
{
    if( m_recordEvents == 1 )
    {
        timeval currentTime, start;
        gettimeofday(&currentTime,NULL);
        timersub(&currentTime,&m_startClock,&start);
        double startD = start.tv_sec + start.tv_usec/1000000.0;
        fprintf( m_commandQueue, "%s %lf\n", "CompositionRenderer::setBlur1()", startD );
    }

    m_forceRedraw = true;

	printf("***** setBlur1 *****\n");

	COMMIT("CompositionRenderer::setBlur1()", blur1() )

    if( m_recordEvents == 1 )
    {
        gettimeofday(&m_startClock,NULL);
    }

}

void
CompositionRenderer::setBlur2()
{
	if( m_recordEvents == 1 )
    {
        timeval currentTime, start;
        gettimeofday(&currentTime,NULL);
        timersub(&currentTime,&m_startClock,&start);
        double startD = start.tv_sec + start.tv_usec/1000000.0;
        fprintf( m_commandQueue, "%s %lf\n", "CompositionRenderer::setBlur2()", startD );
    }

    m_forceRedraw = true;

	printf("***** setBlur2 *****\n");

	COMMIT("CompositionRenderer::setBlur2()", blur2() )

    if( m_recordEvents == 1 )
    {
        gettimeofday(&m_startClock,NULL);
    }

}


void
CompositionRenderer::setBlurImplode()
{
    if( m_recordEvents == 1 )
    {
        timeval currentTime, start;
        gettimeofday(&currentTime,NULL);
        timersub(&currentTime,&m_startClock,&start);
        double startD = start.tv_sec + start.tv_usec/1000000.0;
        fprintf( m_commandQueue, "%s %lf\n", "CompositionRenderer::setBlurImplode()", startD );
    }

    m_forceRedraw = true;

	printf("***** setBlurImplode *****\n");

	COMMIT("CompositionRenderer::setBlurImplode()", blurImplode())

    if( m_recordEvents == 1 )
    {
        gettimeofday(&m_startClock,NULL);
    }

}


void
CompositionRenderer::setOilPaint()
{
    if( m_recordEvents == 1 )
    {
        timeval currentTime, start;
        gettimeofday(&currentTime,NULL);
        timersub(&currentTime,&m_startClock,&start);
        double startD = start.tv_sec + start.tv_usec/1000000.0;
        fprintf( m_commandQueue, "%s %lf\n", "CompositionRenderer::setOilPaint()", startD );
    }

    m_forceRedraw = true;

	printf("***** setOilPaint *****\n");

	COMMIT("CompositionRenderer::setOilPaint()", oilPaint())

    if( m_recordEvents == 1 )
    {
        gettimeofday(&m_startClock,NULL);
    }

}


void
CompositionRenderer::setDft()
{
    if( m_recordEvents == 1 )
    {
        timeval currentTime, start;
        gettimeofday(&currentTime,NULL);
        timersub(&currentTime,&m_startClock,&start);
        double startD = start.tv_sec + start.tv_usec/1000000.0;
        fprintf( m_commandQueue, "%s %lf\n", "CompositionRenderer::setDft()", startD );
    }

    m_forceRedraw = true;

	printf("***** setDft *****\n");

	COMMIT("CompositionRenderer::setDft()", dft())

    if( m_recordEvents == 1 )
    {
        gettimeofday(&m_startClock,NULL);
    }

}

void
CompositionRenderer::setHaldBlur()

{
    if( m_recordEvents == 1 )
    {
        timeval currentTime, start;
        gettimeofday(&currentTime,NULL);
        timersub(&currentTime,&m_startClock,&start);
        double startD = start.tv_sec + start.tv_usec/1000000.0;
        fprintf( m_commandQueue, "%s %lf\n", "CompositionRenderer::setHaldBlur()", startD );
    }

    m_forceRedraw = true;

	printf("***** setHaldBlur *****\n");

	COMMIT("CompositionRenderer::setHaldBlur()", haldBlur())

    if( m_recordEvents == 1 )
    {
        gettimeofday(&m_startClock,NULL);
    }

}

void CompositionRenderer::startCommandClock()
{
	gettimeofday(&m_startCommandClock,NULL);
}

void CompositionRenderer::stopCommandClock(const char s[])
{
	timeval currentTime, elapsed;

	gettimeofday(&currentTime,NULL);

	timersub(&currentTime,&m_startCommandClock,&elapsed);

	double elapsedD = elapsed.tv_sec + elapsed.tv_usec/1000000.0;

	fprintf( m_commandLog, "%s, %lf\n", s, elapsedD );
	fflush( m_commandLog );
}
